﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Xml;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.TeamArchitect.PowerTools;
using Microsoft.VisualStudio.TeamArchitect.PowerTools.Features;
using Microsoft.VisualStudio.TeamArchitect.PowerTools.Features.Design;
using Microsoft.VisualStudio.TeamArchitect.PowerTools.Features.VsTemplateSchema;
using VSLangProj;
using VSLangProj2;


namespace InstantFeatureBuilder
{
    /// <summary>
    /// Visual Studio integration package for the feature.
    /// </summary>
    [PackageRegistration(UseManagedResourcesOnly = true)]
    [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
    [ProvideMenuResource("Menus.ctmenu", 1)]
    [Guid(FeaturePackage.Guid)]
    public sealed class FeaturePackage : Package
    {
        public const string Guid = "cd8531a0-a983-45e8-942e-67d26cfce457";

        [ImportMany(typeof(ILaunchPoint))]
        private IEnumerable<Lazy<ILaunchPoint, IDictionary<string, object>>> LaunchPoints { get; set; }

        EnvDTE.DTE ourDTE;
        EnvDTE.BuildEvents buildEvents;

        protected override void Initialize()
        {
            Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString()));
            base.Initialize();

            var componentModel = this.GetService(typeof(SComponentModel)) as IComponentModel;
            componentModel.DefaultCompositionService.SatisfyImportsOnce(this);

            InitializeVsLaunchPoints();
            ourDTE = this.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE;
            buildEvents = ourDTE.Events.BuildEvents;

            buildEvents.OnBuildBegin += new _dispBuildEvents_OnBuildBeginEventHandler(BuildEvents_OnBuildBegin);

        }

        void BuildEvents_OnBuildBegin(vsBuildScope Scope, vsBuildAction Action)
        {
            if (Action != vsBuildAction.vsBuildActionBuild &&
                Action != vsBuildAction.vsBuildActionRebuildAll)
                return;

            //
            // Here we're going to do the following:
            // 1. Run the Content directory hierarchy and ensure that everything is marked
            //    "Content" and "IncludeInVsix = true"
            // 2. Run the Templates\Projects directory to ensure each .zip file has
            //    the propery WizardTemplate section
            var items = from project in ourDTE.Solution.Projects.Cast<Project>()
                        from item in project.ProjectItems.Cast<EnvDTE.ProjectItem>()
                        where item.Name == "Content"
                        from guidanceItem in item.ProjectItems.Cast<EnvDTE.ProjectItem>()
                        where guidanceItem.Name == "ProcessGuidance"
                        select guidanceItem;

            //
            // Possible values for item.Kind
            //
            //EnvDTE.Constants.vsProjectItemKindMisc
            //EnvDTE.Constants.vsProjectItemKindPhysicalFile
            //EnvDTE.Constants.vsProjectItemKindPhysicalFolder
            //EnvDTE.Constants.vsProjectItemKindSolutionItems
            //EnvDTE.Constants.vsProjectItemKindSubProject
            //EnvDTE.Constants.vsProjectItemKindVirtualFolder

            foreach (var item in items)
            {
                switch (item.Kind)
                {
                    case DTEConstants.vsProjectItemKindPhysicalFile:
                        SetContentCopyAndIncludeInVsix(item, true);
                        break;

                    case DTEConstants.vsProjectItemKindPhysicalFolder:
                        SetContentAndIncludeInVSIX(item.ProjectItems);
                        break;
                }
            }

            var items2 = from project in ourDTE.Solution.Projects.Cast<Project>()
                         from item in project.ProjectItems.Cast<EnvDTE.ProjectItem>()
                         where item.Name == "Content"
                         from guidanceItem in item.ProjectItems.Cast<EnvDTE.ProjectItem>()
                         where guidanceItem.Name == "Data"
                         select guidanceItem;

            //
            // Possible values for item.Kind
            //
            //EnvDTE.Constants.vsProjectItemKindMisc
            //EnvDTE.Constants.vsProjectItemKindPhysicalFile
            //EnvDTE.Constants.vsProjectItemKindPhysicalFolder
            //EnvDTE.Constants.vsProjectItemKindSolutionItems
            //EnvDTE.Constants.vsProjectItemKindSubProject
            //EnvDTE.Constants.vsProjectItemKindVirtualFolder

            foreach (var item in items2)
            {
                switch (item.Kind)
                {
                    case DTEConstants.vsProjectItemKindPhysicalFile:
                        SetContentCopyAndIncludeInVsix(item, true);
                        break;

                    case DTEConstants.vsProjectItemKindPhysicalFolder:
                        SetContentAndIncludeInVSIX(item.ProjectItems);
                        break;
                }
            }

            var projTemplatesItems = from project in ourDTE.Solution.Projects.Cast<Project>()
                                     from item in project.ProjectItems.Cast<EnvDTE.ProjectItem>()
                                     where item.Name == "Templates"
                                     from guidanceItem in item.ProjectItems.Cast<EnvDTE.ProjectItem>()
                                     where guidanceItem.Name == "Projects"
                                     select guidanceItem;

            foreach (var projTemplateItem in projTemplatesItems)
                AddTemplateWizardToZips(projTemplateItem.ProjectItems);

            var itemTemplatesItems = from project in ourDTE.Solution.Projects.Cast<Project>()
                                     from item in project.ProjectItems.Cast<EnvDTE.ProjectItem>()
                                     where item.Name == "Templates"
                                     from guidanceItem in item.ProjectItems.Cast<EnvDTE.ProjectItem>()
                                     where guidanceItem.Name == "Items"
                                     select guidanceItem;

            foreach (var itemTemplateItem in itemTemplatesItems)
                AddTemplateWizardToZips(itemTemplateItem.ProjectItems);

        }

        private void AddTemplateWizardToZips(ProjectItems items)
        {
            foreach (var item in items)
            {
                switch (((EnvDTE.ProjectItem)item).Kind)
                {
                    case DTEConstants.vsProjectItemKindPhysicalFile:
                        EnvDTE.ProjectItem pi = item as EnvDTE.ProjectItem;
                        if (pi.Name.ToLower().EndsWith(".zip") ||
                            pi.Name.ToLower().EndsWith(".vstemplate"))
                        {
                            try
                            {
                                //
                                // Only set the copy/include attributes
                                // if it's a ZIP file (as opposed to a .vstemplate)
                                //
                                if (pi.Name.ToLower().EndsWith(".zip"))
                                    SetContentCopyAndIncludeInVsix(pi, true);

                                //
                                // Be source control savvy since we're actually changing a file's contents
                                //
                                CheckOut(pi.get_FileNames(0));
                                var template = VsTemplateFile.Read(pi.get_FileNames(0)) as VSTemplate;
                                var doc = new XmlDocument();
                                if (template.WizardExtension == null)
                                {
                                    template.WizardExtension = new[]
                                    {
                                        new VSTemplateWizardExtension
                                        {
                                            Assembly = new[] { new XmlNode[] { doc.CreateTextNode(typeof(Microsoft.VisualStudio.TeamArchitect.PowerTools.Features.StandardFeatureTemplateExtension).Assembly.FullName) } },
                                            FullClassName = new[] { new XmlNode[] { doc.CreateTextNode(typeof(Microsoft.VisualStudio.TeamArchitect.PowerTools.Features.StandardFeatureTemplateExtension).FullName) } }
                                        }
                                    };

                                    VsTemplateFile.Write(template);
                                }
                            }
                            catch (Exception e)
                            {
                            }
                        }
                        break;

                    case DTEConstants.vsProjectItemKindPhysicalFolder:
                        AddTemplateWizardToZips(((EnvDTE.ProjectItem)item).ProjectItems);
                        break;
                }
            }
        }



        /// <summary>
        /// Set the Content type and IncludeInVSIX properties
        /// for all the files specified by the incoming collection
        /// </summary>
        /// <param name="items">Collection of ProjectItem object on which to set properties</param>
        private void SetContentAndIncludeInVSIX(ProjectItems items)
        {
            foreach (var item in items)
            {
                switch (((EnvDTE.ProjectItem)item).Kind)
                {
                    case DTEConstants.vsProjectItemKindPhysicalFile:
                        SetContentCopyAndIncludeInVsix(((EnvDTE.ProjectItem)item), true);
                        break;

                    case DTEConstants.vsProjectItemKindPhysicalFolder:
                        SetContentAndIncludeInVSIX(((EnvDTE.ProjectItem)item).ProjectItems);
                        break;
                }
            }
        }

        internal void SetContentCopyAndIncludeInVsix(EnvDTE.ProjectItem projItem, bool include)
        {
            //
            // Set the buildAction to Content
            //
            var buildActionProperty = projItem.Properties.Item("BuildAction");
            buildActionProperty.Value = prjBuildAction.prjBuildActionContent;

            //
            // Set the CopyToOutputDirectory to Copy Always
            //
            var copyToOutputDirectoryProperty = projItem.Properties.Item("CopyToOutputDirectory");
            copyToOutputDirectoryProperty.Value = 1;

            var uniqueName = projItem.ContainingProject.UniqueName;
            var solution = (IVsSolution)GetService(typeof(SVsSolution));
            if (solution != null)
            {
                IVsHierarchy projHierarchy;
                solution.GetProjectOfUniqueName(uniqueName, out projHierarchy);

                uint itemId;
                var fullPath = projItem.get_FileNames(0);

                //get item id for project item
                projHierarchy.ParseCanonicalName(fullPath, out itemId);

                IVsBuildPropertyStorage propertyStorage = projHierarchy as IVsBuildPropertyStorage;
                if (propertyStorage != null)
                {
                    propertyStorage.SetItemAttribute(itemId, "IncludeInVSIX", include.ToString().ToLower());
                }
            }
        }

        private void InitializeVsLaunchPoints()
        {
            var menuCommandService = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
            if (menuCommandService != null)
            {
                var featureLaunchPoints = this.LaunchPoints
                    .Select(lazy => lazy.Value)
                    .OfType<VsLaunchPoint>()
                    .Where(launchPoint => launchPoint.GetType().Assembly == this.GetType().Assembly);

                foreach (var launchPoint in featureLaunchPoints)
                {
                    menuCommandService.AddCommand(launchPoint);
                }
            }
        }

        void CheckOut(string filename)
        {
            if (File.Exists(filename) && File.GetAttributes(filename).HasFlag(FileAttributes.ReadOnly))
            {

                if (ourDTE != null && ourDTE.SourceControl != null &&
                    ourDTE.SourceControl.IsItemUnderSCC(filename) && !ourDTE.SourceControl.IsItemCheckedOut(filename))
                {
                    ourDTE.SourceControl.CheckOutItem(filename);
                }
                else
                {
                    File.SetAttributes(filename, FileAttributes.Normal);
                }
            }
        }

    }

    internal abstract class DTEConstants
    {
        [TypeLibVar(64)]
        public const string dsCPP = "C/C++";
        [TypeLibVar(64)]
        public const string dsFortran_Fixed = "Fortran Fixed";
        [TypeLibVar(64)]
        public const string dsFortran_Free = "Fortran Free";
        [TypeLibVar(64)]
        public const string dsHTML_IE3 = "HTML - IE 3.0";
        [TypeLibVar(64)]
        public const string dsHTML_RFC1866 = "HTML 2.0 (RFC 1866)";
        [TypeLibVar(64)]
        public const string dsIDL = "ODL/IDL";
        [TypeLibVar(64)]
        public const string dsJava = "Java";
        [TypeLibVar(64)]
        public const string dsVBSMacro = "VBS Macro";
        public const string vsAddInCmdGroup = "{1E58696E-C90F-11D2-AAB2-00C04F688DDE}";
        public const string vsCATIDDocument = "{610d4611-d0d5-11d2-8599-006097c68e81}";
        public const string vsCATIDGenericProject = "{610d4616-d0d5-11d2-8599-006097c68e81}";
        public const string vsCATIDMiscFilesProject = "{610d4612-d0d5-11d2-8599-006097c68e81}";
        public const string vsCATIDMiscFilesProjectItem = "{610d4613-d0d5-11d2-8599-006097c68e81}";
        public const string vsCATIDSolution = "{52AEFF70-BBD8-11d2-8598-006097C68E81}";
        public const string vsCATIDSolutionBrowseObject = "{A2392464-7C22-11d3-BDCA-00C04F688E50}";
        public const string vsContextDebugging = "{ADFC4E61-0397-11D1-9F4E-00A0C911004F}";
        public const string vsContextDesignMode = "{ADFC4E63-0397-11D1-9F4E-00A0C911004F}";
        public const string vsContextEmptySolution = "{ADFC4E65-0397-11D1-9F4E-00A0C911004F}";
        public const string vsContextFullScreenMode = "{ADFC4E62-0397-11D1-9F4E-00A0C911004F}";
        public const string vsContextMacroRecording = "{04BBF6A5-4697-11D2-890E-0060083196C6}";
        public const string vsContextMacroRecordingToolbar = "{85A70471-270A-11D2-88F9-0060083196C6}";
        public const string vsContextNoSolution = "{ADFC4E64-0397-11D1-9F4E-00A0C911004F}";
        public const string vsContextSolutionBuilding = "{ADFC4E60-0397-11D1-9F4E-00A0C911004F}";
        public const string vsContextSolutionHasMultipleProjects = "{93694FA0-0397-11D1-9F4E-00A0C911004F}";
        public const string vsContextSolutionHasSingleProject = "{ADFC4E66-0397-11D1-9F4E-00A0C911004F}";
        public const string vsDocumentKindBinary = "{25834150-CD7E-11D0-92DF-00A0C9138C45}";
        public const string vsDocumentKindHTML = "{C76D83F8-A489-11D0-8195-00A0C91BBEE3}";
        public const string vsDocumentKindResource = "{00000000-0000-0000-0000-000000000000}";
        public const string vsDocumentKindText = "{8E7B96A8-E33D-11D0-A6D5-00C04FB67F6A}";
        [TypeLibVar(64)]
        public const string vsext_GUID_AddItemWizard = "{0F90E1D1-4999-11D1-B6D1-00A0C90F2744}";
        [TypeLibVar(64)]
        public const string vsext_GUID_NewProjectWizard = "{0F90E1D0-4999-11D1-B6D1-00A0C90F2744}";
        [TypeLibVar(64)]
        public const string vsext_vk_Code = "{7651A701-06E5-11D1-8EBD-00A0C90F26EA}";
        [TypeLibVar(64)]
        public const string vsext_vk_Debugging = "{7651A700-06E5-11D1-8EBD-00A0C90F26EA}";
        [TypeLibVar(64)]
        public const string vsext_vk_Designer = "{7651A702-06E5-11D1-8EBD-00A0C90F26EA}";
        [TypeLibVar(64)]
        public const string vsext_vk_Primary = "{00000000-0000-0000-0000-000000000000}";
        [TypeLibVar(64)]
        public const string vsext_vk_TextView = "{7651A703-06E5-11D1-8EBD-00A0C90F26EA}";
        [TypeLibVar(64)]
        public const string vsext_wk_AutoLocalsWindow = "{F2E84780-2AF1-11D1-A7FA-00A0C9110051}";
        [TypeLibVar(64)]
        public const string vsext_wk_CallStackWindow = "{0504FF91-9D61-11D0-A794-00A0C9110051}";
        [TypeLibVar(64)]
        public const string vsext_wk_ClassView = "{C9C0AE26-AA77-11D2-B3F0-0000F87570EE}";
        [TypeLibVar(64)]
        public const string vsext_wk_ContextWindow = "{66DBA47C-61DF-11D2-AA79-00C04F990343}";
        [TypeLibVar(64)]
        public const string vsext_wk_ImmedWindow = "{98731960-965C-11D0-A78F-00A0C9110051}";
        [TypeLibVar(64)]
        public const string vsext_wk_LocalsWindow = "{4A18F9D0-B838-11D0-93EB-00A0C90F2734}";
        [TypeLibVar(64)]
        public const string vsext_wk_ObjectBrowser = "{269A02DC-6AF8-11D3-BDC4-00C04F688E50}";
        [TypeLibVar(64)]
        public const string vsext_wk_OutputWindow = "{34E76E81-EE4A-11D0-AE2E-00A0C90FFFC3}";
        [TypeLibVar(64)]
        public const string vsext_wk_PropertyBrowser = "{EEFA5220-E298-11D0-8F78-00A0C9110057}";
        [TypeLibVar(64)]
        public const string vsext_wk_SProjectWindow = "{3AE79031-E1BC-11D0-8F78-00A0C9110057}";
        [TypeLibVar(64)]
        public const string vsext_wk_TaskList = "{4A9B7E51-AA16-11D0-A8C5-00A0C921A4D2}";
        [TypeLibVar(64)]
        public const string vsext_wk_ThreadWindow = "{E62CE6A0-B439-11D0-A79D-00A0C9110051}";
        [TypeLibVar(64)]
        public const string vsext_wk_Toolbox = "{B1E99781-AB81-11D0-B683-00AA00A3EE26}";
        [TypeLibVar(64)]
        public const string vsext_wk_WatchWindow = "{90243340-BD7A-11D0-93EF-00A0C90F2734}";
        public const string vsMiscFilesProjectUniqueName = "<MiscFiles>";
        public const string vsProjectItemKindMisc = "{66A2671F-8FB5-11D2-AA7E-00C04F688DDE}";
        public const string vsProjectItemKindPhysicalFile = "{6BB5F8EE-4483-11D3-8BCF-00C04F8EC28C}";
        public const string vsProjectItemKindPhysicalFolder = "{6BB5F8EF-4483-11D3-8BCF-00C04F8EC28C}";
        public const string vsProjectItemKindSolutionItems = "{66A26722-8FB5-11D2-AA7E-00C04F688DDE}";
        public const string vsProjectItemKindSubProject = "{EA6618E8-6E24-4528-94BE-6889FE16485C}";
        public const string vsProjectItemKindVirtualFolder = "{6BB5F8F0-4483-11D3-8BCF-00C04F8EC28C}";
        public const string vsProjectItemsKindMisc = "{66A2671E-8FB5-11D2-AA7E-00C04F688DDE}";
        public const string vsProjectItemsKindSolutionItems = "{66A26721-8FB5-11D2-AA7E-00C04F688DDE}";
        public const string vsProjectKindMisc = "{66A2671D-8FB5-11D2-AA7E-00C04F688DDE}";
        public const string vsProjectKindSolutionItems = "{66A26720-8FB5-11D2-AA7E-00C04F688DDE}";
        public const string vsProjectKindUnmodeled = "{67294A52-A4F0-11D2-AA88-00C04F688DDE}";
        public const string vsProjectsKindSolution = "{96410B9F-3542-4A14-877F-BC7227B51D3B}";
        public const string vsSolutionItemsProjectUniqueName = "<SolnItems>";
        public const string vsViewKindAny = "{FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF}";
        public const string vsViewKindCode = "{7651A701-06E5-11D1-8EBD-00A0C90F26EA}";
        public const string vsViewKindDebugging = "{7651A700-06E5-11D1-8EBD-00A0C90F26EA}";
        public const string vsViewKindDesigner = "{7651A702-06E5-11D1-8EBD-00A0C90F26EA}";
        public const string vsViewKindPrimary = "{00000000-0000-0000-0000-000000000000}";
        public const string vsViewKindTextView = "{7651A703-06E5-11D1-8EBD-00A0C90F26EA}";
        public const string vsWindowKindAutoLocals = "{F2E84780-2AF1-11D1-A7FA-00A0C9110051}";
        public const string vsWindowKindCallStack = "{0504FF91-9D61-11D0-A794-00A0C9110051}";
        public const string vsWindowKindClassView = "{C9C0AE26-AA77-11D2-B3F0-0000F87570EE}";
        public const string vsWindowKindCommandWindow = "{28836128-FC2C-11D2-A433-00C04F72D18A}";
        public const string vsWindowKindDocumentOutline = "{25F7E850-FFA1-11D0-B63F-00A0C922E851}";
        public const string vsWindowKindDynamicHelp = "{66DBA47C-61DF-11D2-AA79-00C04F990343}";
        public const string vsWindowKindFindReplace = "{CF2DDC32-8CAD-11D2-9302-005345000000}";
        public const string vsWindowKindFindResults1 = "{0F887920-C2B6-11D2-9375-0080C747D9A0}";
        public const string vsWindowKindFindResults2 = "{0F887921-C2B6-11D2-9375-0080C747D9A0}";
        public const string vsWindowKindFindSymbol = "{53024D34-0EF5-11D3-87E0-00C04F7971A5}";
        public const string vsWindowKindFindSymbolResults = "{68487888-204A-11D3-87EB-00C04F7971A5}";
        public const string vsWindowKindLinkedWindowFrame = "{9DDABE99-1D02-11D3-89A1-00C04F688DDE}";
        public const string vsWindowKindLocals = "{4A18F9D0-B838-11D0-93EB-00A0C90F2734}";
        public const string vsWindowKindMacroExplorer = "{07CD18B4-3BA1-11D2-890A-0060083196C6}";
        public const string vsWindowKindMainWindow = "{9DDABE98-1D02-11D3-89A1-00C04F688DDE}";
        public const string vsWindowKindObjectBrowser = "{269A02DC-6AF8-11D3-BDC4-00C04F688E50}";
        public const string vsWindowKindOutput = "{34E76E81-EE4A-11D0-AE2E-00A0C90FFFC3}";
        public const string vsWindowKindProperties = "{EEFA5220-E298-11D0-8F78-00A0C9110057}";
        public const string vsWindowKindResourceView = "{2D7728C2-DE0A-45b5-99AA-89B609DFDE73}";
        public const string vsWindowKindServerExplorer = "{74946827-37A0-11D2-A273-00C04F8EF4FF}";
        public const string vsWindowKindSolutionExplorer = "{3AE79031-E1BC-11D0-8F78-00A0C9110057}";
        public const string vsWindowKindTaskList = "{4A9B7E51-AA16-11D0-A8C5-00A0C921A4D2}";
        public const string vsWindowKindThread = "{E62CE6A0-B439-11D0-A79D-00A0C9110051}";
        public const string vsWindowKindToolbox = "{B1E99781-AB81-11D0-B683-00AA00A3EE26}";
        public const string vsWindowKindWatch = "{90243340-BD7A-11D0-93EF-00A0C90F2734}";
        public const string vsWindowKindWebBrowser = "{E8B06F52-6D01-11D2-AA7D-00C04F990343}";
        public const string vsWizardAddItem = "{0F90E1D1-4999-11D1-B6D1-00A0C90F2744}";
        public const string vsWizardAddSubProject = "{0F90E1D2-4999-11D1-B6D1-00A0C90F2744}";
        public const string vsWizardNewProject = "{0F90E1D0-4999-11D1-B6D1-00A0C90F2744}";
    }
}